package com.ybear.ybutils.utils;

import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.provider.MediaStore;
import android.text.TextUtils;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.util.Consumer;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;

/**
 * 工具类
 */
public class Utils {
    public interface CallbackDelayListener { void onDelay(); }
    private static boolean isClick = true;

    /**
     打开Facebook页面
     @param context     上下文
     @param url         eg:"https://www.facebook.com/页面名称-页面ID"
     @return            打开结果
     */
    public static boolean startFacebookPage(Context context, String url) {
        if( context == null || TextUtils.isEmpty( url ) ) return false;
        try {
            context.getPackageManager().getPackageInfo( "com.facebook.katana", 0 );
            context.startActivity( new Intent(
                    Intent.ACTION_VIEW,
                    Uri.parse( "fb://page/" + url.substring( url.lastIndexOf( "-" ) + 1 ) )
            ));
        } catch (Exception e) {
            try {
                context.startActivity( new Intent( Intent.ACTION_VIEW, Uri.parse( url ) ) );
            }catch(Exception e1) {
                return false;
            }
        }
        return true;
    }

    public static <T> void reverse(T[] arr) {
        int size = arr.length;
        for( int i = 0, mid = size >> 1, j = size - 1; i < mid; i++, j-- ) {
            swap( arr, i, j );
        }
    }

    public static void swap(Object[] arr, int i, int j) {
        Object tmp = arr[ i ];
        arr[ i ] = arr[ j ];
        arr[ j ] = tmp;
    }

    public static String getAssetsString(Context context, String fileName) {
        StringBuilder sb = new StringBuilder();
        InputStreamReader isr;
        BufferedReader br;
        int len;
        try {
            isr = new InputStreamReader( context.getAssets().open(fileName) );
            br = new BufferedReader( isr );
            while ( ( len = br.read() ) != -1 ) {
                sb.append( (char) len );
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sb.toString();
    }

    /**
     * 将Objext转换为List<Map<String, String>
     * @param obj   转换的obj
     * @return      list
     */
    public static List<Map<String, String>> objectToList(Object obj) {
        String key, val;
        List<Map<String, String>> list = new ArrayList<>();
        Map<String, String> map = new HashMap<>();

        if( obj instanceof List) {
            List<?> tmpList = (List<?>) obj;
            //获取列表中的Map（现在是Obj，所以需要转换）
            for( Object mapObj : tmpList ) {
                /* 利用反射获取转换Map */
                Class<?> cls = mapObj.getClass();
                for( Field field : cls.getDeclaredFields() ) {
                    try {
                        Object fieldObj = field.get(mapObj);
                        field.setAccessible(true);
                        key = field.getName();
                        val = fieldObj != null ? fieldObj.toString() : null;
                        map.put(key, val);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                        return null;
                    }
                }
                list.add(map);
            }
        }
        return list;
    }

    /**
     * px转dp
     * @param context       上下文
     * @param px            像素值
     * @return              返回的dp值
     */
    public static float px2Dp(@Nullable Context context, float px) {
        return context == null ? px : px / context.getResources().getDisplayMetrics().density + 0.5f;
    }
    public static int px2Dp(@Nullable Context context, int px) {
        return (int) px2Dp(context, (float) px);
    }

    /**
     * dp转px
     * @param context       上下文
     * @param dp            dip值
     * @return              返回的px值
     */
    public static float dp2Px(@Nullable Context context, float dp) {
        return context == null ? dp : dp * context.getResources().getDisplayMetrics().density + 0.5f;
    }
    public static int dp2Px(@Nullable Context context, int dp) {
        return (int) dp2Px(context, (float) dp);
    }

    /**
     * bmp转字节数组
     * @param bmp           图片资源
     * @param needRecycle   允许被回收（长期持有建议true，否则容易抛异常）
     * @return              字节数组
     */
    public static byte[] bmpToByteArray(Bitmap bmp, boolean needRecycle) {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        bmp.compress(Bitmap.CompressFormat.PNG, 100, output);
        if (needRecycle) {
            bmp.recycle();
        }

        byte[] result = output.toByteArray();
        try {
            output.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return result;
    }
    public static byte[] bmpToByteArray(final Bitmap bmp) {
        return bmpToByteArray( bmp, true );
    }

    /**
     * 图片转Uri路径
     * @param context       上下文
     * @param bmp           转换的图片
     * @return              图片的路径
     */
    public static Uri bmpToUri(Context context, Bitmap bmp) {
        return Uri.parse(MediaStore.Images.Media.insertImage(
                context.getContentResolver(),
                bmp,
                null,
                null
        ));
    }

    /**
     * 图片的路径转图片
     * @param context       上下文
     * @param uri           图片的路径
     * @return              图片
     */
    public static Bitmap uriToBitmap(Context context, Uri uri) {
        Bitmap ret = null;
        ContentResolver cr = context.getContentResolver();
        try {
            ret = MediaStore.Images.Media.getBitmap( cr, uri );
        } catch (IOException e) {
            e.printStackTrace();
        }
        //另一种方法
        if( ret == null ) {
            try {
                ret = BitmapFactory.decodeStream( cr.openInputStream( uri ) );
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
        return ret;
    }

    public static File uriToFile(Context context, Uri uri) {
        String path = null;
        if ( "file".equals( uri.getScheme() ) ) {
            path = uri.getEncodedPath();
            if (path != null) {
                path = Uri.decode(path);
                ContentResolver cr = context.getContentResolver();
                Cursor cur = cr.query(
                        MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                        new String[] {
                                MediaStore.Images.ImageColumns._ID,
                                MediaStore.Images.ImageColumns.DATA
                        },
                        String.format( "(%s='%s')", MediaStore.Images.ImageColumns.DATA, path ),
                        null,
                        null
                );
                int index = 0;
                int dataIdx;
                assert cur != null;
                for (cur.moveToFirst(); !cur.isAfterLast(); cur.moveToNext()) {
                    index = cur.getColumnIndex(MediaStore.Images.ImageColumns._ID);
                    index = cur.getInt(index);
                    dataIdx = cur.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
                    path = cur.getString(dataIdx);
                }
                cur.close();
                if (index != 0) {
                    Uri u = Uri.parse("content://media/external/images/media/" + index);
                    LogUtil.d("uriToFile -> " + u);
                }
            }
            if (path != null) {
                return new File(path);
            }
        } else if ( "content".equals( uri.getScheme() ) ) {
            // 4.2.2以后
            String[] projection = { MediaStore.Images.Media.DATA };
            Cursor cursor = context.getContentResolver().query(
                    uri, projection, null, null, null
            );
            if( cursor == null ) return null;
            if (cursor.moveToFirst()) {
                int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
                path = cursor.getString(columnIndex);
            }
            cursor.close();

            return path != null ? new File(path) : null;
        }
        return null;
    }

    /**
     * 获取缩略图
     * @param bmp       获取缩略图的bmp
     * @return          返回缩略图
     */
    public static Bitmap getThumbnail(@NonNull Bitmap bmp) {
        return getThumbnail( bmp, 500, true );
    }

    public static Bitmap getThumbnail(@NonNull Bitmap bmp, int size, boolean isWidth) {
        int ar = calcAspectRatio( bmp.getWidth(), bmp.getHeight(), size, isWidth );
        return Bitmap.createScaledBitmap(
                bmp,
                isWidth ? size : ar,
                isWidth ? ar : size,
                true
        );
    }

    /**
     * 通过uri获取缩略图
     * @param context       上下文
     * @param uri           原图的uri路径
     * @return              返回缩略图
     */
    public static Bitmap getThumbnail(final Context context, Uri uri) {
        //Uri转Bitmap
        Bitmap ret = uriToBitmap( context, uri );
        return ret != null ? getThumbnail( ret ) : null;
    }

    /**
     * 将uri转为真实路径
     * @param mContext      上下文
     * @param uri           uri路径
     * @return 返回真实路径
     */
    public static String getPathFromUri(Context mContext, Uri uri) {
        String str = null;
        String path = uri == null ? null : uri.getPath();
        Cursor cursor;
        int index;
        String[] projection = { MediaStore.Images.Media.DATA };
        ContentResolver cr = mContext.getContentResolver();
        if (uri != null) {
            cursor = cr.query(uri, projection, null, null, null);
            if (cursor != null) {
                //移动游标到第一个位置
                cursor.moveToFirst();
                //获取索引
                index = cursor.getColumnIndexOrThrow( projection[0] );
                if (cursor.getCount() > 0) {
                    str = cursor.getString(index);//获取当前位置的数据
                }
                cursor.close();
            }
            //4.4+解决办法
            if (str == null) {
                str = GetPathFromUri4kitkat.getPath(mContext, uri);
            }
        }
        return str == null ? path : str;
    }

    /**
     * 文件大小
     * @param file      文件
     * @return 返回文件大小（-1获取失败）
     */
    public static int getFileSize(File file) {
        try {
            FileInputStream fis = new FileInputStream(file);
            return fis.available();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return -1;
    }

    /**
     * map转set
     * @param map   Map
     * @return Set
     */
    public static <T> Set<T> map2Set(Map<String, T> map) {
        Set<T> ret = new HashSet<>();
        for( String key : map.keySet() ) ret.add( map.get( key ) );
        return ret;
    }

    /**
     * 点击间隔延迟
     * @param mCallbackIntervalDelayListener    回调
     */
    public static void clickIntervalDelay(int delay, final CallbackDelayListener
            mCallbackIntervalDelayListener) {
        if( isClick ) {
            isClick = false;
            //点击间隔延迟
            new Timer().schedule(new TimerTask() {
                @Override
                public void run() {
                    isClick = true;
                    mCallbackIntervalDelayListener.onDelay();
                }
            }, delay);
        }
    }
    public static void clickIntervalDelay(CallbackDelayListener mCallbackIntervalDelayListener) {
        clickIntervalDelay(500, mCallbackIntervalDelayListener);
    }

    private static long mFirstTime = 0;
    public static void doubleClickListener(Consumer<Boolean> call) {
        long secondTime = System.currentTimeMillis();
        boolean isDoubleClick;
        if( secondTime - mFirstTime > 2000 ) {
            mFirstTime = secondTime;
            isDoubleClick = false;
        } else {
            mFirstTime = 0;
            isDoubleClick = true;
        }
        if( call != null ) call.accept( isDoubleClick );
    }

    /**
     * 计算长宽比
     * @param fromWidth         原始宽度
     * @param fromHeight        原始高度
     * @param toWidthOrHeight   目标宽度或高度
     * @param isWidth           <code>toWidthOrHeight</code>是宽度还是高度
     * @return                  <code>isWidth</code>为true则返回结果为高度，否则为宽度
     */
    public static int calcAspectRatio(int fromWidth, int fromHeight,
                                      int toWidthOrHeight, boolean isWidth) {
        double ret;
        double ratio = (double) fromHeight / (double) fromWidth;
        if( isWidth ) {
            ret = (double) toWidthOrHeight * ratio;
        }else {
            ret = (double) toWidthOrHeight / ratio;
        }
        return (int) ret;
    }

    /**
     * 为空抛异常，否则返回
     * @param obj   检查的obj
     * @param <T>   当前obj的反射
     * @return      obj
     */
    public static <T> T requireNonNull(T obj) {
        if( obj != null ) return obj;
        throw new NullPointerException("this is Null");
    }

    public static Object getField(Object obj, String fieldName) {
        if( obj == null || fieldName == null || "".equals( fieldName ) ) return false;
        Field field;
        try {
            field = obj.getClass().getDeclaredField(fieldName);
            field.setAccessible( true );
            return field.get( obj );
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static boolean setField(Object obj, String fieldName, Object val) {
        if( obj == null || fieldName == null || "".equals( fieldName ) || val == null ) return false;
        Field field;
        try {
            field = obj.getClass().getDeclaredField(fieldName);
            field.setAccessible( true );
            field.set(obj, val);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 反射调用方法
     * @param obj           调用类
     * @param methodName    方法名
     * @param args          参数
     * @return              返回值
     */
    public static Object invokeMethod(Object obj, String methodName,
                                      Object[] args, Class<?>... argsType) {
        if( obj == null || methodName == null || "".equals( methodName ) ) return null;
        try {
            if( args == null || args.length == 0 ||
                    argsType == null || args.length != argsType.length ) {
                return obj.getClass().getMethod( methodName ).invoke( obj );
            }else {
                //调用
                return obj.getClass().getMethod(methodName, argsType).invoke( obj, args);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static Object invokeMethod(Object obj, String methodName) {
        return invokeMethod( obj, methodName, null );
    }
}
